package com.tianji.learning.service.impl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.msg.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import java.util.List;

@RequiredArgsConstructor
@Service
public class SignRecordServiceImpl implements ISignRecordService {
    final StringRedisTemplate redisTemplate;
    final RabbitMqHelper rabbitMqHelper;
    @Override
    public SignResultVO addSignRecords() {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        //2.获取今天的日期年月
        LocalDate now = LocalDate.now();
        String today = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.SGIN_RECORD_KEY_PREFIX + userId + ":" + today;
        //3.签到之前校验是否已经签到
        Boolean aBoolean = redisTemplate
                .opsForValue()
                .setBit(key, LocalDate.now().getDayOfMonth() - 1, true);
        if (aBoolean!=null&&aBoolean){
            //4.1已经签到，返回签到结果
            throw new BizIllegalException("已经签到，不能重复签到");
        }
        //4.封装返回
        SignResultVO result = new SignResultVO();
        //4.1连续签到
        //4.1.1得到连续签到天数，根据连续签到天数获取奖励积分
        int signDays = countSignDays(key, now.getDayOfMonth());
        result.setSignDays(signDays);
        int rewardPoints = 0;
        switch (signDays) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        result.setRewardPoints(rewardPoints);

        //TODO 保存签到积分
        rabbitMqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId, rewardPoints + 2));// 签到积分是基本得分+奖励积分

        return result;
    }

    @Override//查询签到记录
    public Byte[] selectMonthSignRecords() {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        //2.获取今天的日期年月
        LocalDate now = LocalDate.now();
        String today = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.SGIN_RECORD_KEY_PREFIX + userId + ":" + today;
       //bitfield key get u（这个月第几天-1） 0
        List<Long> longs = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(now.getDayOfMonth())).valueAt(0));
        if (CollUtils.isEmpty(longs)){
            throw new BizIllegalException("查询异常");
        }
        Long num = longs.get(0);
        int offset = now.getDayOfMonth()-1;
        // 返回结果
        Byte[] res = new Byte[now.getDayOfMonth()];
        while (offset>=0){
            res[offset]=(byte) (num&1);
            offset--;
            num>>=1;
        }

        return res;
    }

    private int countSignDays(String key, int dayOfMonth) {

        //获取签到数据
        // 1.获取本月从第一天开始，到今天为止的所有签到记录
        //BITFIELD key GET encoding offset
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
            if (CollUtils.isEmpty(result)){
                return 0;
            }
        //2.遍历签到数据，统计连续签到天数
        int resultValue = result.get(0).intValue();
            int count = 0;
            while ((resultValue&1)==1){
                count++;
                resultValue>>=1;
            }
            return count;
    }
}
